package zw_101_200.zw_190_颠倒二进制位;

public class Solution {

    public static void main(String[] args) {
        int n = 11111111;
        int num = reverseBits(n);
        System.out.println(num);
    }

    private static final int M1 = 0x55555555; // 01010101010101010101010101010101
    private static final int M2 = 0x33333333; // 00110011001100110011001100110011
    private static final int M4 = 0x0f0f0f0f; // 00001111000011110000111100001111
    private static final int M8 = 0x00ff00ff; // 00000000111111110000000011111111

    /**
     * 位运算分治
     * 思路
     * 若要翻转一个二进制串，可以将其均分成左右两部分，对每部分递归执行翻转操作，然后将左半部分拼在右半部分的后面，即完成了翻转。
     * 由于左右两部分的计算方式是相似的，利用位掩码和位移运算，我们可以自底向上地完成这一分治流程。
     * 对于递归的最底层，我们需要交换所有奇偶位：
     *      取出所有奇数位和偶数位；
     *      将奇数位移到偶数位上，偶数位移到奇数位上。
     * 类似地，对于倒数第二层，每两位分一组，按组号取出所有奇数组和偶数组，然后将奇数组移到偶数组上，偶数组移到奇数组上。以此类推。
     * 需要注意的是，在某些语言（如Java）中，没有无符号整数类型，因此对 n 的右移操作应使用逻辑右移。
     * 复杂度分析
     * 时间复杂度：O(1)。
     * 空间复杂度：O(1)。
     * 另外分治其实也不难理解: 简化原理如下:
     *      原数据为:12345678
     *      第一轮 奇偶位交换 21436587
     *      第二轮 每两位交换 43218765
     *      第三轮 每四位交换 87654321
     * @param n
     * @return
     */
    public static int reverseBits(int n) {
        n = n >>> 1 & M1 | (n & M1) << 1;
        n = n >>> 2 & M2 | (n & M2) << 2;
        n = n >>> 4 & M4 | (n & M4) << 4;
        n = n >>> 8 & M8 | (n & M8) << 8;
        return n >>> 16 | n << 16;
    }



    /**
     * 对称位
     * 一个简单的做法是对输入的 n 做逐位检查。
     * 如果某一位是 1 的话，则将答案相应的对称位置修改为 1。
     * 时间复杂度：int 固定 32 位，循环次数不随输入样本发生改变。复杂度为 O(1)
     * 空间复杂度：O(1)
     * @param n
     * @return
     */
//    public static int reverseBits(int n) {
//        int ans = 0;
//        for (int i = 0; i < 32; i++) {
//            int t = (n >> i) & 1;
//            if (t == 1) {
//                ans |= (1 << (31 - i));
//            }
//        }
//        return ans;
//    }

    /**
     * 逐位分离
     * 每次都使用 n 的最低一位，使用 n 的最低一位去更新答案的最低一位，使用完将 n 进行右移一位，将答案左移一位。
     * 相当于每次都用 n 的最低一位更新成 ans 的最低一位。
     * 时间复杂度：int 固定 32 位，循环次数不随输入样本发生改变。复杂度为 O(1)
     * 空间复杂度：O(1)
     * @param n
     * @return
     */
//    public static int reverseBits(int n) {
//        int ans = 0;
//        int cnt = 32;
//        while (cnt-- > 0) {
//            ans <<= 1;
//            ans += (n & 1);
//            n >>= 1;
//        }
//        return ans;
//    }

    /**
     * 分组互换
     * 对于长度固定的 intint 类型，我们可以使用「分组构造」的方式进行。
     * 两位互换 -> 四位互换 -> 八位互换 -> 十六位互换。
     * 时间复杂度：如何进行互换操作取决于 int 长度。复杂度为 O(1)
     * 空间复杂度：O(1)
     * @param n
     * @return
     */
//    public static int reverseBits(int n) {
//        n = ((n & 0xAAAAAAAA) >>> 1)  | ((n & 0x55555555) << 1);
//        n = ((n & 0xCCCCCCCC) >>> 2)  | ((n & 0x33333333) << 2);
//        n = ((n & 0xF0F0F0F0) >>> 4)  | ((n & 0x0F0F0F0F) << 4);
//        n = ((n & 0xFF00FF00) >>> 8)  | ((n & 0x00FF00FF) << 8);
//        n = ((n & 0xFFFF0000) >>> 16) | ((n & 0x0000FFFF) << 16);
//        return n;
//    }



}

